/*
 * CHttpTokenAuth.cpp
 *
 *  Created on: 2018年2月9日
 *      Author: terry
 */

#include "CHttpTokenAuth.h"
#include <functional>
#include "TStringCast.h"
#include <time.h>


namespace impl
{

struct CheckUpdateExpiration : public std::binary_function<std::string, CHttpTokenAuth::TokenEntry,bool>
{
	explicit CheckUpdateExpiration(size_t expiration):
		m_expiration(expiration)
	{
	}

	bool operator ()(const std::string& key, CHttpTokenAuth::TokenEntry& entry)
	{
		bool checked = false;
		time_t curTime = time(NULL);
		if (entry.expiration >= curTime)
		{
			entry.expiration = curTime + m_expiration;
			checked = true;
		}
		return checked;
	}

	size_t m_expiration;
};

struct IsExpired : public std::binary_function<std::string, CHttpTokenAuth::TokenEntry,bool>
{
	explicit IsExpired(time_t curTime):
		m_curTime(curTime)
	{
	}

	bool operator ()(const std::string& key, CHttpTokenAuth::TokenEntry& entry)
	{
		bool checked = false;
		return (entry.expiration < m_curTime);
	}

	time_t m_curTime;
};

}

CHttpTokenAuth::CHttpTokenAuth():
	m_expiration(EXPIRATION),
	m_seq(0),
	m_checkTime()
{
}

CHttpTokenAuth::~CHttpTokenAuth()
{
}

void CHttpTokenAuth::setExpiration(size_t seconds)
{
	m_expiration = seconds;
}

void CHttpTokenAuth::setUserStore(HttpUserStorePtr store)
{
	m_store = store;
}

bool CHttpTokenAuth::login(const std::string& username, const std::string& password, std::string& token)
{
	if (!m_store)
	{
		return false;
	}

	HttpUser user;
	if (!m_store->findUser(username, user))
	{
		return false;
	}

	if (user.password != password)
	{
		return false;
	}

	unsigned int rand = time(NULL) << 8;
	unsigned int num = ++ m_seq + rand;
	token = comn::StringCast::toString(num);

	TokenEntry entry;
	entry.username = username;
	entry.token = token;
	entry.expiration = time(NULL) + m_expiration;
	m_tokenMap.put(token, entry);

	return true;
}

void CHttpTokenAuth::logout(const std::string& token)
{
	m_tokenMap.remove(token);
}

bool CHttpTokenAuth::check(const std::string& token, std::string& username)
{
	time_t curTime = time(NULL);
	if ((curTime - m_checkTime) > (m_expiration * 2))
	{
		clearExpired();
	}

	impl::CheckUpdateExpiration checker(m_expiration);
	TokenEntry entry;
	int state = m_tokenMap.checkRemove(token, entry, checker);
	if (state == TokenMap::kTrue)
	{
		username = entry.username;
	}
	return (state == TokenMap::kTrue);
}

void CHttpTokenAuth::clear()
{
	m_tokenMap.clear();
}

size_t CHttpTokenAuth::size()
{
	return m_tokenMap.size();
}

bool CHttpTokenAuth::getAt(size_t idx, std::string& username, std::string& token)
{
	TokenEntry entry;
	bool found = m_tokenMap.getAt(idx, token, entry);
	if (found)
	{
		username = entry.username;
	}
	return found;
}

void CHttpTokenAuth::clearExpired()
{
	impl::IsExpired func(time(NULL));
	m_tokenMap.removeAll(func);
}
